/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package asset.load;

import com.jme3.animation.Animation;
import com.jme3.animation.BoneTrack;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import asset.ActionSet;
import asset.Skel;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import util.math.Convert;
import util.xml.XML;

/**
 *
 * @author
 */
public class LoadActionSet {

    /**
     *
     * @param skel provided only to translate bone names to bone indices
     * @param file
     * @return
     * @throws Exception
     */
    public static ActionSet load(Skel skel, String file) throws Exception {
        Worker w = new Worker(skel, file);
        w.load();
        return w.getActionSet();
    }

    private static class Worker {

        Skel skel;
        String file;
        //
        ActionSet actionSet;

        public Worker(Skel skel, String file) {
            this.skel = skel;
            this.file = file;
        }

        public ActionSet getActionSet() {
            return actionSet;
        }

        private void load() throws Exception {
            Document doc = XML.openDocument(file);

            float timePerFrame = 1.0f / 24;

            List<Animation> animations = new ArrayList<>();
            for (Node actionNode : XML.iterate(doc.getFirstChild().getChildNodes())) {

                int numFrames = 0;
                {
                    int frameStart = XML.getInt(actionNode, "frame_start");
                    int frameEnd = XML.getInt(actionNode, "frame_end");

                    // We ignore whether frameStart is positive or negative. We
                    // position all keyframes in "positive time".
                    numFrames = (frameEnd - frameStart) + 1;
                }

                // Go over the bones
                List<BoneTrack> boneTracks = new ArrayList<>();
                for (Node boneNode : XML.iterate(actionNode.getChildNodes())) {
                    // Go over all keyframes
                    float[] times = new float[numFrames];
                    Vector3f[] translations = new Vector3f[numFrames];
                    Quaternion[] rotations = new Quaternion[numFrames];
                    Vector3f[] scales = new Vector3f[numFrames];

                    int i = 0;
                    for (Node frameNode : XML.iterate(boneNode.getChildNodes())) {

                        Node locNode = frameNode.getFirstChild();
                        Node rotNode = locNode.getNextSibling();
                        Node scaleNode = rotNode.getNextSibling();

                        times[i] = i * timePerFrame;
                        translations[i] = Convert.toVector3f(XML.getFloatArray(locNode, "x", "y", "z"));

                        // Important notice: the XML data should be so that the bones
                        // tip (it's "up" direction) is Z. For Blender export
                        // this requires a slight modification because in Blender
                        // the bone tip corresponds to Y.
                        rotations[i] = Convert.toQuaternion(XML.getFloatArray(rotNode, "w", "x", "y", "z"));
                        scales[i] = Convert.toVector3f(XML.getFloatArray(scaleNode, "x", "y", "z"));

                        i += 1;
                    }

                    // Create a bone track
                    String boneName = XML.getStr(boneNode, "name");
                    int boneIndex = skel.getBoneIndex(boneName);
                    BoneTrack boneTrack = new BoneTrack(boneIndex);
                    boneTrack.setKeyframes(times, translations, rotations, scales);

                    boneTracks.add(boneTrack);
                }

                // Put all BoneTracks in one Animation
                String actionName = XML.getStr(actionNode, "name");
                Animation animation = new Animation(actionName, numFrames * timePerFrame);
                animation.setTracks(boneTracks.toArray(new BoneTrack[0]));
                animations.add(animation);
            }

            this.actionSet = new ActionSet(animations);
        }
    }
}
